#!/bin/bash

function errcho {
    echo "$@" 1>&2;
}

function pkgname {
    if [ -z $1 ]; then
        errcho "Missing argument 1 (package group)";
        exit 1;
    elif [ -z $2 ]; then
        errcho "Missing argument 2 (package search pattern)";
        exit 2;
    fi
    local pkg_entry=$(_pkgentry $1 $2) || exit 3;
    local col=$(echo $pkg_entry | wc -w);
    if (( $col > 0 )); then
        NAMEPARTS=$(echo $pkg_entry | tr -s '[:space:]' ' ' | cut -d' ' -f1);
		N=$(echo $NAMEPARTS | tr '-' ' ' | wc -w);
		if ((N>1)); then
			echo $(echo $NAMEPARTS | cut --complement -d'-' -f$N);
		else
			echo $NAMEPARTS;
		fi
    else
        errcho "pkgname: no package found $1/$2";
        exit 4;
    fi
}

function pkgversion {
    if [ -z $1 ]; then
        errcho "Missing argument 1 (package group)";
        exit 1;
    elif [ -z $2 ]; then
        errcho "Missing argument 2 (package search pattern)";
        exit 2;
    fi
    local pkg_entry=$(_pkgentry $1 $2) || exit 3;
    local col=$(echo $pkg_entry | wc -w);
    if (( $col > 0 )); then
        local nameparts=$(echo $pkg_entry | tr '[:blank:]' ':' | cut -d: -f1 | tr -s '-' ' ');
		local n=$(echo $nameparts | wc -w);
		if (( $n > 1 )); then
			local v=$(echo $nameparts | cut -d' ' -f$n);
			if [[ $v != "" ]]; then
				echo $v;
			else
				errcho "pkgversion: can not get version info of $2 from group $1";
				exit 4;
			fi
		else
			errcho "pkgversion: no version info $2";
			exit 5;
		fi
	else
        errcho "pkgversion: no package found $2";
        exit 6;
    fi
}

function pkgarchive {
    if [ -z $1 ]; then
        errcho "Missing argument 1";
        exit 1;
    elif [ -z $2 ]; then
        errcho "Missing argument 2";
        exit 2;
    fi
    local pkg_entry=$(_pkgentry $1 $2) || exit 3;
    local col=$(echo $pkg_entry | wc -w);
    if (( $col > 0 )); then
        echo $(echo $pkg_entry | cut -d' ' -f2);
    else
        errcho "Can not get an archive information of $2 from group $1";
		exit 4;
    fi
}

function _pkgentry {
    cd $LFS/sources/pkg
    if [ -r $1.package.list ]; then
        PKG_LIST=$(cat $1.package.list | tr -s '[:blank:]' ':' | tr -s '[:space:]' '\n' | tr -s ':' ' ' | grep '^[^#]' | grep -o "^$2-.*");
        IFS=$'\n';
        for p in $PKG_LIST; do
            pn=$(echo $p | cut -d' ' -f1);
            N=$(echo $pn | tr '-' ' ' | wc -w);
            if ((N<=1)) && [[ $pn == $2 ]] || \
                [[ $(echo $pn | cut -d'-' --complement -f$N) == $2 ]]
            then
                echo "$p";
            fi
        done
    else
        errcho "Package list not found: $1.package.list";
        exit 1;
    fi
}

function pkgdeps {
	if [ -z $1 ]; then
        errcho "pkgdeps: missing argument 1 (package name)";
        return 1;
    fi
    PKG_EXTRA="";
    PKG_BASIC="";
    PKG_EXTRA=$(pkgname extra $1) || PKG_BASIC=$(pkgname basic $1) || return 2;
    if [ ! -z $PKG_EXTRA ]; then
        PKG_GROUP="extra";
        PKG_NAME=$PKG_EXTRA;
        PKG_VERSION=$(pkgversion extra $PKG_NAME);
        PKG_ARCHIVE=$(pkgarchive extra $PKG_NAME);
    elif [ ! -z $PKG_BASIC ]; then
        PKG_GROUP="basic";
        PKG_NAME=$PKG_BASIC;
        PKG_VERSION=$(pkgversion basic $PKG_NAME);
        PKG_ARCHIVE=$(pkgarchive basic $PKG_NAME);
    else
        errcho "pkgdeps: couldn't find package $1 in basic nor extra source data base";
        return 5;
    fi
	if [ ! -e /sources/pkg/$PKG_GROUP/make_$PKG_NAME.sh ]; then
		errcho "pkgdeps: couldn't find package make script /sources/pkg/$PKG_GROUP/make_$PKG_NAME.sh";
		return 6;
	fi
	if [ -r /sources/pkg/$PKG_GROUP/$PKG_NAME/deps.pkg ]; then
		cat /sources/pkg/$PKG_GROUP/$PKG_NAME/deps.pkg | tr -s '[:blank:]' ':' | tr -s '[:space:]' '\n' | tr ':' ' ' | grep -v '^#' || echo "";
	fi
}

function pkgdepstree {
	if [ -z $1 ]; then
        errcho "pkgdepstree: missing argument 1 (package name)";
        return 1;
    fi
	if [ -z $2 ]; then
        errcho "pkgdepstree: missing argument 2 (package version)";
        return 2;
    fi
	if [ ! -z $3 ]; then
        if [[ $3 != "missing" ]]; then
            errcho "pkgdepstree: argument 3 unknwon ('missing' --> list only not installed packages)";
            return 3;
        fi
    fi
    _pkgclearcache || return 4;
	_pkgdepstree $1 $2 $3;
	ERR=$?;
	return $ERR;
}

function _pkgaddconflict {
    [ -z $1 ] && return 1;
    [[ $(cat /tmp/pkgconflictcache | grep "^${1}$") == "" ]] && echo $1 >> /tmp/pkgconflictcache;
    return 0;
}

function pkggetconflicts {
    [ -r /tmp/pkgconflictcache ] && \
        cat /tmp/pkgconflictcache 2> /dev/null;
}

function _pkgclearcache {
    rm -f /tmp/pkgconflictcache 2> /dev/null;
    rm -f /tmp/pkgtreecache 2> /dev/null;
    rm -f /tmp/pkgcirclecache 2> /dev/null;
    touch /tmp/pkgconflictcache && \
    touch /tmp/pkgtreecache && \
    touch /tmp/pkgcirclecache;
    return $?
}

function _pkgwascached {
    [ -z $1 ] && return 1;
    [[ $(cat /tmp/pkgtreecache | grep "^${1}$") != "" ]] && return 0;
    return 10;
}

function _pkgaddcache {
    [ -z $1 ] && return 1;
    echo $1 >> /tmp/pkgtreecache;
    return 0;
}

function _pkgpush {
    [ -z $1 ] && return 1;
    [[ $(cat /tmp/pkgcirclecache | grep "^${1}$") != "" ]] && {
        errcho "_pkgpush: circle dependency between $(cat /tmp/pkgcirclecache | grep -v '^$' | tail -1) and $1 found, abort ($(cat /tmp/pkgcirclecache | grep -v '^$' | tr -s [:space:] '>')$1)";
        return 10;
    }
    echo $1 >> /tmp/pkgcirclecache;
    #errcho $(cat /tmp/pkgcirclecache | grep -v '^$' | tr -s [:space:] ' > ');
    return 0;
}

function _pkgpop {
    N=$(($(cat /tmp/pkgcirclecache | grep -v '^$' | wc -l)-1));
    if ((N<=0)); then
        echo "" > /tmp/pkgcirclecache;
    else
        cat /tmp/pkgcirclecache | grep -v '^$' | head -$N > /tmp/pkgcirclecache;
    fi
    return $?
}

function _pkgdepstree {
    _pkgpush $1 || { errcho "pkgdepstree: error ($?) while processing dependencies of $1"; return 6; };
    _pkgwascached $1 || {
        FOUND_DEPS="";
        PKG_DEPS="$(pkgdeps $1 $2)" || { errcho "pkgdepstree: error ($?) while processing dependencies of $1 $2"; return 3; };
        #errcho "DEBUG: pkgdeps $1 $2 --> $(echo "$PKG_DEPS" | tr -s [:blank:] '-' | tr -s [:space:] ' ')";
        IFS=$'\n';
        for p in $PKG_DEPS; do
            PKG_NAME=$(echo $p | cut -d' ' -f1);
            if [[ $PKG_NAME != "!"* ]]; then
                PKG_VERSION=$(echo $p | cut -d' ' -f2);
                INSTVERS=$(pkginstver $PKG_NAME);
                INSTVER=$(versionmatch $PKG_VERSION $INSTVERS 2> /dev/null);
                if [[ $INSTVER != "" ]]; then
                    if [[ $3 == "missing" ]]; then
                        # dependency alread installed - ignore package
                        #errcho "pkgdepstree: $PKG_NAME already installed"
                        continue;
                    fi
                    # sub dependencies based on the greatest, installed package version
                    SUBDEPS=$(_pkgdepstree $PKG_NAME $INSTVER $3) || return 4;
                    # set package version
                    PKG_VERSION=$INSTVER;
                else
                    # sub dependencies based on the given package version from package dependency file
                    SUBDEPS=$(_pkgdepstree $PKG_NAME $PKG_VERSION $3) || return 5;
                fi
                errcho "|$PKG_NAME $PKG_VERSION| --> $(echo "$SUBDEPS" | tr -s [:blank:] '-' | tr -s [:space:] ' ')";
                
                if [[ $SUBDEPS == "" ]]; then
                    FOUND_DEPS=$(echo "$FOUND_DEPS"; echo $PKG_NAME $PKG_VERSION);
                else
                    FOUND_DEPS=$(echo "$FOUND_DEPS"; echo "$SUBDEPS");
                    FOUND_DEPS=$(echo "$FOUND_DEPS"; echo $PKG_NAME $PKG_VERSION);
                fi
            else
                _pkgaddconflict $(echo $PKG_NAME | sed -e 's|^!||');
            fi
        done
        if [[ $FOUND_DEPS != "" ]]; then
            echo "$FOUND_DEPS" | grep -v '^$';
        fi
        _pkgaddcache $1 || { errcho "pkgdepstree: error ($?) while processing dependencies of $1"; return 8; };
    }
    _pkgpop || { errcho "pkgdepstree: error ($?) while processing dependencies of $1"; return 7; };
}

function pkgmakechain {
	if [ -z $1 ]; then
        errcho "pkgmakechain: missing argument 1 (package name)";
        return 1;
    fi
	if [ -z $2 ]; then
        errcho "pkgmakechain: missing argument 2 (package version)";
        return 2;
    fi
    PKG_DEPS="$(pkgdepstree $1 $2 missing)" || { errcho "pkgmakechain: error while processing dependency tree of $1 $2"; return 3; };
	PKG_DEPS="$(echo "$PKG_DEPS")";
    echo "$PKG_DEPS"
}

function unpack {
	if [ -z $1 ]; then
		errcho "unpack: missing argument 1 (archive)";
		return 1;
	fi
	local new_folder="";
	cd $LFS/sources > /dev/null && \
	for f in ./${1}.{tar*,tgz,zip}; do
		if [ -f $f ]; then
            if [[ $f == *".zip" ]]; then
                new_folder=$1;
            else
                new_folder=$(tar -t -f $f | sed -e 's|^./||' | grep '.*/' | head -n1 | cut -d/ -f1);
            fi
            if (($? != 0)); then
                errcho "unpack: error while processing archive: $f, try next file...";
                continue;
            fi
            if [ -z $new_folder ]; then
                errcho "unpack: can not determine archive destination folder, try nect file...";
                continue;
            fi
			errcho "unpack: destination folder: $f --> $new_folder";
            rm -fR $new_folder > /dev/null;
			if [[ $f == *".tar.gz" || $f == *".tgz" ]]; then
				tar -xvvzf $f > /dev/null;
			elif [[ $f == *".tar.xz" ]]; then
				tar -xvvf $f > /dev/null;
			elif [[ $f == *".tar.bz2" ]]; then
				tar -xvvjf $f > /dev/null;
            elif [[ $f == *".tar" ]]; then
                tar -xvvf $f > /dev/null;
            elif [[ $f == *".zip" ]]; then
                mkdir -p $new_folder > /dev/null && \
                unzip $f -d $new_folder > /dev/null || rm -fR $new_folder > /dev/null;
			else
				errcho "unpack: unsupported archive $f";
				return 2;
			fi
			if (($? != 0)); then
				errcho "unpack: error while decompressing $f, abort";
				return 3;
			fi
		fi
	done
	if [[ $new_folder == "" || ! -d $new_folder ]]; then
		errcho "unpack: missing destination folder ($new_folder) after processing archive $1";
		return 4;
	else
		echo $new_folder;
        return 0;
	fi
}

function mver {
	if [ -z $1 ]; then
		errcho "Missing argument... 1 (version number x.y or x.y.z)";
		return 1;
	fi
	if (( $(echo $1 | tr '.' ' ' | wc -w) <= 1 )); then
		echo $(echo $1 | tr -d [:space:] | tr -d '.');
	else
		echo $(echo $1 | sed -e 's|^\([0-9]*\).*|\1|');
	fi
}

function mmver {
	if [ -z $1 ]; then
		errcho "Missing argument... 1 (version number x.y or x.y.z)";
		return 1;
	fi
	if (( $(echo $1 | tr '.' ' ' | wc -w) < 2 )); then
		echo $1;
	else
		echo $(echo $1 | sed -e 's/^\([0-9]*\).\([0-9]*\).*/\1.\2/');
	fi
}

function versionmatch {
    #errcho "versionmatch $1 $2"
	if [ -z $1 ]; then
        errcho "versionmatch: missing argument 1 (version required)";
        return 1;
    fi
	if [ -z $2 ]; then
        errcho "versionmatch: missing argument 2 (version to compare)";
        return 2;
    fi
    
    # get all arguments except the first
    COMP_VER=$(echo $@ | cut --complement -d' ' -f1);
    
    RES=9;
    for v in $COMP_VER; do
        _versionmatch $1 $v;
        RES=$?;
        if ((RES==0)) || ((RES==10)); then
            echo $v
            return $RES;
        fi
    done
    return $RES;
}

function _versionmatch {

	if [[ $1 == $2 ]]; then
		errcho "version required ($1) is equal with version to compare ($2)";
		return 0;
	fi

	VR=$(echo $1 | tr -d '[:space:]');
	VC=$(echo $2 | tr -d '[:space:]');

	VRN=$(echo $VR | tr '.' ' ' | wc -w);
	VCN=$(echo $VC | tr '.' ' ' | wc -w);
	
	if [ $VRN -le $VCN ]; then
		N=$VRN;
	else
		N=$VCN;
	fi
	
	#errcho "VR=$VR VC=$VC VRN=$VRN VCN=$VCN"

	if ((N == 0)); then
		errcho "versionmatch: illegal version format(s) -> $VR $VC";
		return 3;
	fi

	for (( n=1; n<=N; n++ )); do
		VER1=$(echo $VR | cut -d. -f$n);
		VER2=$(echo $VC | cut -d. -f$n);
		#errcho "VER1=$VER1 VER2=$VER2"
		
		if [[ $VER1 == $VER2 ]] && ((n>=N)) && ((VRN==VCN)); then
			errcho "version required ($VR) is equal with version to compare ($VC)";
			return 0;
		elif [[ $(echo $VER1 | tr -d '[:alpha:]') != $VER1 || $(echo $VER2 | tr -d '[:alpha:]') != $VER2 ]]; then
			# catch none numeric
			if [[ $VER1 != $VER2 ]]; then
				errcho "version required ($VR) and version to compare ($VC) not compareable at position $n";
				return 8;
			fi
		elif (( VER1 < VER2 )); then
			errcho "version required ($VR) is less than version to compare ($VC)";
			return 10;
		elif (( VER1 > VER2 )); then
			errcho "version required ($VR) is greater than version to compare ($VC)";
			return 11;
		fi
	done
	
	if ((VRN > VCN)); then
		errcho "version required ($VR) is longer than version to compare ($VC)";
		return 4;
	elif ((VRN < VCN)); then
		errcho "version required ($VR) is shorter than version to compare ($VC)";
		return 5;
	else
		errcho "version required ($VR) dosn't match version to compare ($VC)";
		return 6;
	fi
}

function saferm {
    if [ -z $1 ]; then
        errcho "Missing argument 1";
        return 1;
	fi
	if [[ $(echo $1 | grep '^/.*$') == "" ]]; then
		errcho "$1 pointing to root or an relative position... abort";
		return 2;
	fi
	if [[ $(echo $1 | grep '^/tools\|^/sources\|^/usr/pkg') != "" ]]; then
		errcho "$1 pointing to an protected folder... /tools /sources or /usr/pkg ... abort";
		return 3;
	fi
	if [ -d $1 ]; then
		if [[ $(ls -A $1) == "" ]]; then
			rm -d $1 || return 4;
		else
			errcho "$1 is not empty... abort";
			return 5;
		fi
	elif [ -e $1 ] || [ -h $1 ]; then
		# remove files and symbolic links
		rm -f $1 || return 6;
	else
		errcho "$1 dosn't exists";
		return 10;
	fi
	[ -e $1 ] && errcho "$1 couldn't be removed" && return 11 || echo "$1 removed";
	return 0;
}
